#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <error.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include "config.h"
#include "protocol.h"
#include "mysql_util.h"
#include "log_util.h"


#define BACK_LOG 10

int parse_arg(char *str, char args[][32])
{
	char *ptr,*retptr;
    int i = 0;
	
	ptr = str;
 	ljf_info("str:%s\n", str);
	
    while ((retptr=strtok(ptr, ",")) != NULL) {
		if(i < 4)
		{
			memcpy(args[i],retptr,strlen(retptr));
		}
		
		ljf_debug("substr[%d]:%s\n", i, retptr);

		i++;
        ptr = NULL;
    }
 	
    return i;
}

int process_request_msg(
	char *recv_msg, 
	int msg_len, 
	struct sockaddr_in client, 
	msg_process_result_type *result
)
{
	int ret = -1;
	
	int len = 0;
	int resp_len = 0;
	char response[1024] = {0};
	iot_req_data_type *iot_data = NULL;
	iot_resp_data_type iot_resp;
	if(recv_msg == NULL)
	{
		ljf_err("recv_msg is NULL\n");
		return ret;
	}

	if(result == NULL)
	{
		ljf_err("result is NULL\n");
		return ret;
	}
	
	//compare protocol header
	iot_data = (iot_req_data_type *)recv_msg;
	ljf_info("recv msg from client header: %s,cmd type:%d data_len:%d\n", iot_data->header, iot_data->cmd, iot_data->data_len);	
	//parse arg and process cmd function
	switch(iot_data->cmd)
	{
		case e_cmd_auth:
		{
			iot_auth_data_type auth_data;
			iot_resp.cmd = e_cmd_auth_ack;
			memset(&auth_data, 0x00, sizeof(iot_auth_data_type));
			memcpy(&auth_data, iot_data->data, iot_data->data_len);

			if(!check_dev_access(&auth_data))
			{
				ljf_info("device [%s] have acces to iotserver\n", auth_data.dev_id);
				memcpy(result->dev_id, auth_data.dev_id, sizeof(auth_data.dev_id));
				result->auth_result = e_auth_pass;
				iot_resp.result = e_success;
				resp_len = sprintf(iot_resp.resp_str, "%s", "OK");
			}
			else
			{
				ljf_info("device [%s] have NO acces to iotserver\n", auth_data.dev_id);
				iot_resp.result = e_failed;
				result->auth_result = e_auth_invalid_secret;
				resp_len = sprintf(iot_resp.resp_str, "%s", "Reject");
			}
		}
		break;
		case e_cmd_thinfo:
		{
			char args[4][32]={0};
			iot_resp.cmd = e_cmd_thinfo_ack;
			if(result->auth_result == e_auth_pass) // only auth pass can save data
			{
				ljf_info("msg data:%s\n",iot_data->data);
				len = parse_arg(iot_data->data, args);
				/*If args is 4 add to mysql*/
				if(len == 4)
				{
					add_thinfo(args[0], args[1], args[2], args[3], inet_ntoa(client.sin_addr));
					iot_resp.result = result->opt_result = e_success;
					
					resp_len = sprintf(iot_resp.resp_str, "%s", "OK");
				}
				else
				{
					ljf_err("Invalid args number:%d\n",len);
					iot_resp.result = result->opt_result = e_failed;
					resp_len = sprintf(iot_resp.resp_str, "%s", "Invalid");
				}
			}
			else
			{
				resp_len = sprintf(iot_resp.resp_str, "%s", "Reject");
			}
		}
		break;
		case e_cmd_bye:
		{
			iot_resp.cmd = e_cmd_bye_ack;
			if(memcmp(result->dev_id,iot_data->data,iot_data->data_len))
			{
				ljf_info("matched!!");
				result->auth_result = e_auth_min;
				memset(result->dev_id,0x00,sizeof(result->dev_id));
				iot_resp.result = 0;
				resp_len = sprintf(iot_resp.resp_str, "%s", "Bye");
			}
			else
			{
				ljf_info("dismatched!!");
				iot_resp.result = 1;
				resp_len = sprintf(iot_resp.resp_str, "%s", "Fuck");
			}
		}
		break;
		default:
			ljf_err("Invalid cmd:%d\n", iot_data->cmd);
		break;
	}
	ret = 0;
	
	//send response
	send(result->connectfd, (void *)&iot_resp, sizeof(iot_resp), 0);
	return ret;
}

int main()
{
    int listenfd,connectfd;
    struct sockaddr_in server;
    struct sockaddr_in client;
    pid_t childpid;
    socklen_t addrlen;
    char buff[4096];  
	ljf_info("Log model init\n");
	if(init_file_point())
	{
		ljf_err("log_init failed,exit...\n");
		exit(1);
	}
	
	ljf_info("Log model init OK\n");
	
	ljf_info("mysql model init\n");
	if (init_mysql(g_host_name, g_user_name, g_password, g_db_name, g_db_port))
	{
        print_mysql_error(NULL);
		ljf_err("mysql init failed,exit...\n");
		exit(1);
	}
	ljf_info("mysql model init OK\n");

	ljf_info("server network model init\n");
    listenfd = socket(AF_INET,SOCK_STREAM,0);
    if(listenfd == -1)
	{
        perror("socker created failed");
        exit(0);
    }
    int option;
    option = SO_REUSEADDR;
    setsockopt(listenfd,SOL_SOCKET,option,&option,sizeof(option));
    bzero(&server,sizeof(server));
    server.sin_family = AF_INET;
    server.sin_port = htons(IOT_LISTEN_PORT);
    server.sin_addr.s_addr = htonl(INADDR_ANY);
    if(bind(listenfd,(struct sockaddr *)&server,sizeof(server)) == -1){
       	perror("Bind error!");
       	exit(1);
    }
    if(listen(listenfd,BACK_LOG) == -1){
       	perror("listend error");
       	exit(1);
    }
	ljf_info("Server network model init OK.\n");
	ljf_info("Listening on port:%d\n", IOT_LISTEN_PORT);
	ljf_info("Waiting for clinet's request.....\n");
	
    while(1)
	{
    		int n;
    		addrlen = sizeof(client);
    		connectfd = accept(listenfd, (struct sockaddr*)&client, &addrlen);
    		if(connectfd == -1)
			{
        		ljf_err("accept error");
        		exit(0);
    		}
			else
			{
    			ljf_info("client connected\n");
    		}
    		if((childpid = fork()) == 0)
			{
    			close(listenfd);
    			ljf_info("client from %s\n",inet_ntoa(client.sin_addr));
				msg_process_result_type result;
				memset(&result,0x00,sizeof(result));
    			while((n = read(connectfd, buff, 4096)) > 0)
				{
					int response_len = 0;
					result.connectfd = connectfd;
					response_len = process_request_msg(buff, n, client, &result);
					
    			}
    			ljf_info("end read\n");
                exit(0);
    		}
			else if(childpid < 0)
			{
				ljf_info("fork error: %s\n", strerror(errno));
			}
			else
			{
				;//Do nothing for father process
			}
    		
    		close(connectfd);
	}
	
	deinit_mysql();
    return 0;
}

